package com.example.datamanage.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.datamanage.common.CodeEnum;
import com.example.datamanage.common.Constants;
import com.example.datamanage.common.Result;
import com.example.datamanage.common.UserStatusEnum;
import com.example.datamanage.exception.MyException;
import com.example.datamanage.mapper.UserMapper;
import com.example.datamanage.pojo.User;
import com.example.datamanage.pojo.dto.LoginDTO;
import com.example.datamanage.pojo.dto.RegisterDTO;
import com.example.datamanage.pojo.dto.UserDTO;
import com.example.datamanage.pojo.dto.UserSearchDTO;
import com.example.datamanage.service.MenuService;
import com.example.datamanage.service.UserService;
import com.example.datamanage.utils.JwtUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.util.Date;
import java.util.List;

/**
* @author 28142
* @description 针对表【sys_user】的数据库操作Service实现
* @createDate 2023-07-17 14:37:12
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
    implements UserService{
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private MenuService menuService;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public void export(HttpServletResponse response) {
        // 查询用户列表
        List<User> list = list();
        try {
            // 在内存操作，写到浏览器
            ExcelWriter writer = ExcelUtil.getWriter(true);

            // 一次性将list内的对象写到excel，使用默认样式
            writer.write(list, true);

            // 设置浏览器相应格式
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
            String fileName = URLEncoder.encode("用户信息表", "UTF-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");

            ServletOutputStream outputStream = response.getOutputStream();
            writer.flush(outputStream, true);
            outputStream.close();
            writer.close();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public UserDTO login(LoginDTO loginDTO) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getUsername, loginDTO.getUsername());
        User user;
        // 如果查询结果不止一条，则提示系统错误
        try {
            user = userMapper.selectOne(wrapper);
        } catch (Exception e) {
            throw new MyException(CodeEnum.CODE_500.getCode(), CodeEnum.CODE_500.getMsg());
        }
        // 如果数据库中没有记录或者密码比对错误，提示
        if (user == null || !passwordEncoder.matches(loginDTO.getPassword(), user.getPassword())) {
            throw new MyException(CodeEnum.CODE_400.getCode(), "用户名或密码错误");
        }
        UserDTO userDTO = new UserDTO();
        // 将user的属性拷贝到userDTO中，hutool的属性拷贝没有生效，因此用spring自带的
        BeanUtils.copyProperties(user, userDTO);
        // 根据角色获取菜单列表
        userDTO.setMenuList(menuService.getRoleMenu(user.getRole()));
        // 生成token
        String token = JwtUtils.createToken(user);
        userDTO.setToken(token);
        return userDTO;
    }

    @Override
    public boolean register(RegisterDTO registerDTO) {
        if (!registerDTO.getPassword().equals(registerDTO.getConfirmPwd())) {
            throw new MyException(CodeEnum.CODE_400.getCode(), "两次密码输入不一致");
        }
        if (!registerDTO.getCode().equals(stringRedisTemplate.opsForValue().get(registerDTO.getEmail()))) {
            throw new MyException(CodeEnum.CODE_402.getCode(), "验证码不匹配");
        }
        // 查询数据库判断是否已注册
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getUsername, registerDTO.getUsername());
        User user = userMapper.selectOne(wrapper);
        // 如果数据库中没有记录，则插入记录
        if (user == null) {
            user = new User();
            BeanUtils.copyProperties(registerDTO, user);
            // 密码加密
            user.setPassword(passwordEncoder.encode(user.getPassword()));
            // 默认角色为普通用户并启用账号
            user.setRole(Constants.ROLE_USER);
            user.setStatus(UserStatusEnum.ENABLE.getValue());
            // 新增用户
            return save(user);
        } else {
            throw new MyException(CodeEnum.CODE_500.getCode(), "用户已存在");
        }
    }

    @Override
    public Result findPage(Integer pageNum, Integer pageSize, UserSearchDTO searchDTO) {
        String username = searchDTO.getUsername();
        String startTime = searchDTO.getStartTime();
        String endTime = searchDTO.getEndTime();
        Boolean status = searchDTO.getStatus();
        // 查询条件，过滤password字段
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(User.class, user -> !"password".equals(user.getColumn()))
                .like(StrUtil.isNotBlank(username), User::getUsername, username)
                .between(StrUtil.isNotBlank(startTime) && StrUtil.isNotBlank(endTime), User::getCreateTime, startTime, endTime)
                .eq(status != null, User::getStatus, status);
        // 分页
        Page<User> page = new Page<>(pageNum, pageSize);
        return Result.success(userMapper.selectPage(page, wrapper));
    }

    @Override
    public boolean saveUser(User user) {
        // 如果id为null，表示新增，设置初始密码；如果是修改，则设置修改时间
        if (user.getId() == null) {
            // 判断用户是否存在
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(User::getUsername, user.getUsername());
            User one = userMapper.selectOne(wrapper);
            if (one == null) {
                user.setPassword(passwordEncoder.encode(Constants.INIT_PWD));
            } else {
                throw new MyException(CodeEnum.CODE_400.getCode(), "用户已存在，请换一个用户名");
            }
        } else {
            user.setUpdateTime(new Date());
        }
        return saveOrUpdate(user);
    }

}




